#!/usr/bin/python

"""
File: test_wrapper.py

Author: Wolfgang Resch
Modifications by: Jesse Noller

Location: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/525484 

Desc: Two convenience classes for applying different scenarios (data) to 
different functions and measuring timing as a practical framework for optimizing performance.

Example (see also _test()):

>>> from timer_decorator import *
>>> @Timer
... def test_true(a):
...     \"\"\" The function must have a doc string \"\"\"
...     if a:
...         return True
...     return False
... 
>>> suite = Scenario(test_1={'desc' : "Test to see if an argument is true!", 
...                           'args' : (True,), 'functions' : (test_true,) })    
...
>>> suite.run() #doctest: +SKIP

Output (Note, doctest chokes on the times spit out by run, TBF)
--------------------------------------------------
TEST_1
Test to see if an argument is true!
     The function must have a doc string                    0.00310ms [0.00310 - 0.00310]

"""

from time import clock, time

class Timer(object):
    """ 
    Timer decorator. This is the actual function that the test function is
    decorated with. The resulting object will have a timer report attached to
    it. 
    """
    def __init__(self, function):
        self.__function = function
        self.__name__ = function.__name__
        self.__doc__ = function.__doc__
        self.times = []
    def __call__(self, *args, **kwargs):
        start = time()
        result = self.__function(*args, **kwargs)
        stop = time()
        self.times.append(stop-start)

    def report(self):
        
        results = {'min': min(self.times)*1000, 'max':max(self.times)*1000,
                   'mean':sum(self.times)/len(self.times)*1000}
                   
        return self.__doc__.ljust(50) + "\t%(mean)6.5fms [%(min)6.5f - %(max)6.5f]" % results

    def reset(self):
        self.times = []

class Scenario(object):
    """ Scenario / "test-suite" execution  """
    def __init__(self, **kw):
        """
        pass each scenario as a kw={'desc':"", data:(,), functions:(,)}
        for each scenario each function is applied to the data and results are printed
        """
        self.scenarios = kw
        
    def run(self):
        for title, scenario in self.scenarios.items():
            print 50*'-' + "\n" + title.upper()
            print scenario['desc']
            for f in scenario['functions']:
                f(*scenario['args'])
                print "\t" + f.report()
                f.reset()

def _test():
    """ Simple test and example. """
    @Timer
    def test_true(a):
        """ The function must have a doc string """
        if a:
            return True
        return False
 
    suite = Scenario(test_1={'desc' : "Test to see if an argument is true!", 
                             'args' : (True,), 'functions' : (test_true,) })
    suite.run()    

if __name__ == "__main__":
    _test()